package my.actuate.autoconfigure;

import java.util.Objects;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaManagementContextConfiguration;
import org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration;
import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMappingCustomizer;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import my.actuate.beans.BeansMvcEndpoint;
import my.actuate.beans.BeansService;
import my.actuate.beans.support.ConfigurationBeanVOBuilder;
import my.actuate.beans.support.GenericBeanVOBuilder;
import my.actuate.beans.support.GenericPropertyVOBuilder;

@ManagementContextConfiguration
@AutoConfigureAfter(EndpointWebMvcHypermediaManagementContextConfiguration.class)
public class CustomActuatorsConfiguration {

    public static final String CONFIG_PREFIX_ACTUATOR_ADMIN_BEANS ="actuator.admin.beans";
    @Bean
    @ConditionalOnEnabledEndpoint("admin/beans")
    @ConfigurationProperties(prefix = CONFIG_PREFIX_ACTUATOR_ADMIN_BEANS + ".beansMvcEndpoint")
    BeansMvcEndpoint beansMvcEndpoint() {
        BeansMvcEndpoint beansMvcEndpoint = new BeansMvcEndpoint();
        // add 
        return beansMvcEndpoint;
    }




    @Bean
    @ConfigurationProperties(prefix = CONFIG_PREFIX_ACTUATOR_ADMIN_BEANS + ".beansService")
    @ConditionalOnBean(BeansMvcEndpoint.class)
    @Lazy
    BeansService beansService() {
        BeansService beansService = new BeansService();
        return beansService;
    }



    @Bean
    @Lazy
    @Primary
    @ConfigurationProperties(prefix = CONFIG_PREFIX_ACTUATOR_ADMIN_BEANS + ".beanview.builder.default")
    @ConditionalOnBean(BeansService.class)
//    @Scope(proxyMode=ScopedProxyMode.TARGET_CLASS)
    GenericBeanVOBuilder defaultBeanVOBuilder() {
        // default BeanVOBuilder.
        return new GenericBeanVOBuilder() {
        };
    }



    @Bean
    @Lazy
    @ConfigurationProperties(prefix = CONFIG_PREFIX_ACTUATOR_ADMIN_BEANS + ".beanview.builder.configuration")
    @ConditionalOnBean(BeansService.class)
    ConfigurationBeanVOBuilder configurationBeanVOBuilder(ApplicationContext pContext) {
        return new ConfigurationBeanVOBuilder() {
            @Override
            public String[] getIds() {
                return pContext.getBeanNamesForAnnotation(Configuration.class);
            }
        };
    }



    @Bean
    @Lazy
    @Primary
    @ConfigurationProperties(prefix = CONFIG_PREFIX_ACTUATOR_ADMIN_BEANS + ".propertyview.builder")
    @ConditionalOnBean(BeansService.class)
    GenericPropertyVOBuilder defaultPropertyVOBuilder() {
        return new GenericPropertyVOBuilder() {
            
        };
    }



    @Bean
    @ConditionalOnBean({BeansMvcEndpoint.class})
    EndpointHandlerMappingCustomizer commonEndpointHandlerMappingCustomizer(BeansMvcEndpoint pBeansMvcEndpoint) {
        return new EndpointHandlerMappingCustomizer() {
            @Override
            public void customize(EndpointHandlerMapping pMapping) {
                pMapping.setInterceptors(new HandlerInterceptorAdapter() {
                    @Override
                    public boolean preHandle(HttpServletRequest pRequest, HttpServletResponse pResponse, Object pHandler) throws Exception {
                        boolean valid = super.preHandle(pRequest, pResponse, pHandler);
                        if (!valid) return false;
                        HandlerMethod handlerMethod = (HandlerMethod) pHandler;
                        if (handlerMethod.getBean() instanceof BeansMvcEndpoint) {
                            if (!pBeansMvcEndpoint.isEnabled()) {
                                pResponse.sendError(HttpStatus.INTERNAL_SERVER_ERROR.value(), "BeansMvcEndpoint enabled false");
                                return false;
                            }
                        }
                        return true;
                    }



                    @Override
                    public void postHandle(HttpServletRequest pRequest, HttpServletResponse pResponse, Object pHandler,
                        ModelAndView pModelAndView) throws Exception {
                        super.postHandle(pRequest, pResponse, pHandler, pModelAndView);
                        HandlerMethod handlerMethod = (HandlerMethod) pHandler;
                        if (HttpMethod.OPTIONS.matches(pRequest.getMethod()) && !(handlerMethod.getBean() instanceof BeansMvcEndpoint)) {
                            return;
                        }
                        MvcEndpoint mvcEndpoint = (MvcEndpoint) handlerMethod.getBean();
                        if (pModelAndView != null && Objects.equals(pBeansMvcEndpoint.getPath(), mvcEndpoint.getPath())) {
                            pModelAndView.addObject("beansUrlPrefix", pBeansMvcEndpoint.getBeansUrlPrefix());
                        }
                    }
                });
            }
        };

    }
}
